//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<XPanel.cpp>
///	@path	~/src/gui/xgui/
///	@date	2008/11/16
///	@desc	.

#define _FULLAPI
#include "config/config.h"

#include "lib/utilities/observer.h"

#include "gui/xgui/XPanel.h"
#include "gui/composite/composition.h"

namespace xeres {

	IMPL_IOBJECT_CLASS_DYNAMIC( XPanel );
	
	// XPanel
	XPanel::XPanel( void )
		: GuiSyleComp( new CGuiStyle )
	{
	}
	// XPanel
	XPanel::XPanel( const Rect<int>& placement )
		: XForm( placement )
		, GuiSyleComp( new CGuiStyle )
		, m_edgeSize( -1 , -1 )
	{
	}
	// XPanel
	XPanel::XPanel( const Pair<int>& size )
		: XForm( size )
		, GuiSyleComp( new CGuiStyle )
		, m_edgeSize( -1 , -1 )
	{

	}
	// XPanel
	XPanel::XPanel( RefWeak<IBitmap> background )
		: XForm( background.IsValid() ? background->GetSize() : Pair<int>(0,0) )
		, GuiSyleComp( new CGuiStyle )
		, m_edgeSize( -1 , -1 )
	{
	}
	// ~XPanel
	XPanel::~XPanel( void )
	{
	}
	// GetStyle
	RefWeak<CTextStyle> XPanel::GetStyle( void )
	{
		return m_guiStyle;
	}
	// SetSize
	bool XPanel::SetSize( const Pair<int>& size )
	{
		if( m_background.IsValid() && m_edgeSize.x < 0 && m_edgeSize.y < 0 )
		{
			TRACE_ERROR( _S("XPanel::SetSize: Panel has a fixed size background, size cannot be set.") );
			return false;
		}
		return __super::SetSize( size );
	}
	// SetEdgeSize
	bool XPanel::SetEdgeSize( const Pair<int>& edge )
	{
		if( m_background.IsNull() )
		{
			TRACE_ERROR( _S("XPanel::SetEdgeSize: No background image, failed to set edge size.") );
			return false;
		}
		if( edge.x * 2 >= (int)m_background->GetWidth() || edge.y * 2 >= (int)m_background->GetHeight() )
		{
			TRACE_ERROR( _S("XPanel::SetEdgeSize: Invalid edge size to match image size.") );
			return false;
		}
		m_edgeSize = edge;
		return true;
	}
	// GetEdgeSize
	Pair<int> XPanel::GetEdgeSize( void ) const
	{
		return m_edgeSize;
	}
	// SetBackground
	void XPanel::SetBackground( RefWeak<IBitmap> background )
	{
		// should check ~
		m_background = background;
		// set to fixed size image.
		m_edgeSize.set( -1 , -1 );
		if( m_background.IsValid() )
		{
			__super::SetSize( m_background->GetSize() );
		}
		else
		{
			__super::SetSize( Pair<int>(100,100) );
		}
	}
	// SetBackground
	void XPanel::SetBackground( RefWeak<IBitmap> background , const Pair<int>& edge )
	{
		// should check ~
		m_background = background;
		SetEdgeSize( edge );

		if( m_background.IsValid() )
		{
			__super::SetSize( m_background->GetSize() );
		}
		else
		{
			__super::SetSize( Pair<int>(100,100) );
		}
	}
	// GetBackground
	RefWeak<IBitmap> XPanel::GetBackground( void )
	{
		return m_background;
	}
	// OnKeyInput
	HandleInput XPanel::OnKeyInput( Updater& updater )
	{
		HandleInput result = __super::OnKeyInput( updater );
		if( result == INPUT_HANDLED )
			ChangeStyle();
		return result;
	}
	// OnMouseInput
	HandleInput XPanel::OnMouseInput( Updater& updater )
	{
		HandleInput result = __super::OnMouseInput( updater );
		if( result == INPUT_HANDLED )
			ChangeStyle();
		return result;
	}
	// OnEnable
	void XPanel::OnEnable( bool enable )
	{
		__super::OnEnable( enable );
		ChangeStyle();
	}
	// OnFocus
	void XPanel::OnFocus( bool focusing )
	{
		__super::OnFocus( focusing );
		ChangeStyle();
	}
	// OnMouseOver
	void XPanel::OnMouseOver( bool hovering )
	{
		__super::OnMouseOver( hovering );
		ChangeStyle();
	}
	// ChangeStyle
	void XPanel::ChangeStyle( void )
	{
		RefWeak<CGuiStyle> old = m_activeGuiStyle;
		ResetStyle( m_enable , m_clicked , m_hover , m_focus );
		if( m_activeGuiStyle != old )
		{
			TRACE_INFO( _S("ChangeStyel to update control.") );
			UpdateControl();
		}
	}
	// OnDrawBackground
	void XPanel::OnDrawBackground( Composition& composition )
	{
		if( m_background.IsValid() )
		{
			// draw background
			if( m_edgeSize.x >= 0 && m_edgeSize.y >= 0 )
			{
				composition.painter().StcBitmap( m_background , Pair<int>(0,0) , GetBound() , 255 , 0 , m_edgeSize.x , m_edgeSize.y );
			}
			else
			{
				composition.painter().DrawBitmap( m_background , Pair<int>(0,0) , GetBound() , 255 , 0 );
			}
		}
		else
		{
			ARGB color = m_activeGuiStyle->m_guiBackgroundColor;
			if( color.a > 0 )
			{
				composition.graphics().FillRectangle(
					&Gdiplus::SolidBrush( Gdiplus::Color( color.a , color.r, color.g , color.b ) ) ,
					toRect( GetBound() ) );
			}
		}
	}

	IMPL_IOBJECT_CLASS_DYNAMIC( XScrollPanel );

	// XScrollPanel
	XScrollPanel::XScrollPanel( void )
		: XPanel()
	{
		InitForm();
	}
	// XScrollPanel
	XScrollPanel::XScrollPanel( const Rect<int>& placement )
		: XPanel( placement )
	{
		InitForm();
	}
	// XScrollPanel
	XScrollPanel::XScrollPanel( const Pair<int>& size )
		: XPanel( size )
	{
		InitForm();
	}
	// XScrollPanel
	XScrollPanel::XScrollPanel( RefWeak<IBitmap> background )
		: XPanel( background )
	{
		InitForm();
	}
	// ~XScrollPanel
	XScrollPanel::~XScrollPanel( void )
	{
		m_verticalScroll = NULL;
		m_horizontalScroll = NULL;
		m_clientPanel;
	}
	// InitForm
	void XScrollPanel::InitForm( void )
	{
		if( m_verticalScroll.IsNull() ) m_verticalScroll = new XScrollBarVertical;
		if( m_horizontalScroll.IsNull() ) m_horizontalScroll = new XScrollBarHorizontal;
		if( m_clientPanel.IsNull() ) m_clientPanel = new XPanel;
		m_clientPanel->SetOffset( Pair<int>(0,0) );
		m_verticalScroll->SetScrollCall( bind( &XScrollPanel::OnVerticalScroll , this , _1 ) );
		m_horizontalScroll->SetScrollCall( bind( &XScrollPanel::OnHorizontalScroll , this , _1 ) );
		m_children.clear();
 		XPanel::AddChild( m_verticalScroll );
 		XPanel::AddChild( m_horizontalScroll );
 		XPanel::AddChild( m_clientPanel );
	}
	// OnVerticalScroll
	void XScrollPanel::OnVerticalScroll( float pos )
	{
		//TRACE_INFO( _S("Scroll Pos: %f") , pos );
		const Pair<int> offset = m_clientPanel->GetOffset();
		m_clientPanel->SetOffset( Pair<int>( offset.x , -int(pos) ) );
	}
	// OnHorizontalScroll
	void XScrollPanel::OnHorizontalScroll( float pos )
	{
		const Pair<int> offset = m_clientPanel->GetOffset();
		m_clientPanel->SetOffset( Pair<int>( -int(pos) , offset.y ) );
	}
	// AddChild
	bool XScrollPanel::AddChild( RefWeak<IGuiComp> comp )
	{
		return m_clientPanel->AddChild( comp );
	}
	// DelChild
	bool XScrollPanel::DelChild( RefWeak<IGuiComp> comp )
	{
		return m_clientPanel->DelChild( comp );
	}
	// GetChild
	RefWeak<IGuiComp> XScrollPanel::GetChild( int index )
	{
		return m_clientPanel->GetChild( index );
	}
	// SetSize
	bool XScrollPanel::SetSize( const Pair<int>& size )
	{
		//return __super::SetSize( size );
		m_verticalScroll->SetVisible( false );
		m_horizontalScroll->SetVisible( false );

		if( IGuiControl::SetSize( size ) )
		{
			Rect<int> bound = GetBound();
			int left = bound.left();
			int top = bound.top();
			int right = bound.right();
			int bottom = bound.bottom();
			// edge affect
			const Pair<int>& edge = GetEdgeSize();
			if( edge.x > 0 )
			{
				left += edge.x;
				right -= edge.x;
			}
			if( edge.y > 0 )
			{
				top += edge.y;
				bottom -= edge.y;
			}

			const int VERTICAL_SCROLL = 20;
			const int HORIZONTAL_SCROLL = 20;

			m_clientPanel->SetAdptiveSize( Pair<int>( right - left , bottom - top ) );
			Pair<int> formSize = m_clientPanel->GetSize();

			bool horizontal = formSize.x > size.x;
			bool vertical = formSize.y > size.y;
			if( horizontal || vertical )
			{
				if( vertical )
				{
					m_clientPanel->SetAdptiveSize( Pair<int>( right - left - VERTICAL_SCROLL , formSize.y ) );
					formSize = m_clientPanel->GetSize();
					if( !horizontal )
					{
						horizontal = formSize.x > size.x;
					}
				}
				if( horizontal )
				{
					m_clientPanel->SetAdptiveSize( Pair<int>( formSize.x , bottom - top - HORIZONTAL_SCROLL ) );
					formSize = m_clientPanel->GetSize();
					if( !vertical )
					{
						vertical = formSize.y > size.y;
						if( vertical )
						{
							m_clientPanel->SetAdptiveSize( Pair<int>( right - left - VERTICAL_SCROLL , formSize.y ) );
							formSize = m_clientPanel->GetSize();
						}
					}
				}

				// see if vertical needs
				if( vertical )
				{
					m_verticalScroll->SetVisible( true );
					m_verticalScroll->SetPlacement( Rect<int>(
						right - VERTICAL_SCROLL ,
						top , right ,
						bottom - ( horizontal ? HORIZONTAL_SCROLL : 0 ) ) );
				}
				// see if horizontal needs
				if( horizontal )
				{
					m_horizontalScroll->SetVisible( true );
					m_horizontalScroll->SetPlacement( Rect<int>(
						left ,
						bottom - HORIZONTAL_SCROLL ,
						right - ( vertical ? VERTICAL_SCROLL : 0 ) ,
						bottom ) );
				}

				int page_x = right - left - ( vertical ? VERTICAL_SCROLL : 0 );
				int page_y = bottom - top - ( horizontal ? HORIZONTAL_SCROLL : 0 );
				// set real form size.
				m_clientPanel->SetSize( Pair<int>( page_x , page_y ) );

				m_horizontalScroll->SetRange( float( formSize.x - page_x ) , float( page_x ) );
				m_verticalScroll->SetRange( float( formSize.y - page_y) , float( page_y ) );
				// calculate offset
			}
			return true;
		}
		return false;
	}
	// SetClientPanel
	bool XScrollPanel::SetClientPanel( RefWeak<XPanel> panel )
	{
		if( panel.IsNull() )
		{
			panel = new XPanel;
		}
		XPanel::DelChild( m_clientPanel );
		m_clientPanel = panel;
		XPanel::AddChild( m_clientPanel );
		return true;
	}
	// GetClientPanel
	RefWeak<XPanel> XScrollPanel::GetClientPanel( void )
	{
		return m_clientPanel;
	}

} // namespace xeres
